<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Common\Robot\Robot;
use App\CustHub\Constants\ContactBehaviorTypeConstants;
use App\CustHub\Constants\ContactDedupTaskStatusConstants;
use App\CustHub\Mapper\ChContactDedupTaskMapper;
use App\CustHub\Mapper\ChContactMapper;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Model\ChContact;
use App\CustHub\Model\ChContactBehaviorLog;
use App\CustHub\Model\ChContactDedupTask;
use App\Robot\Dto\CommonJobDto;
use App\Robot\Job\CommonJob;
use App\Robot\Service\RobotApiService;
use Carbon\Carbon;
use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\Coroutine\Coroutine;
use Hyperf\Database\Model\Model;
use Hyperf\Database\Query\Builder;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use Mine\Annotation\Transaction;
use Mine\Exception\NormalStatusException;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Coroutine\co;
use function Hyperf\Support\optional;

/**
 * 客户去重
 */
class ChContactDedupService extends AbstractService
{
    public $mapper;

    #[Inject]
    protected ChContactMapper $contactMapper;

    #[Inject]
    protected ChRobotMapper $robotMapper;

    public function __construct(ChContactDedupTaskMapper $mapper)
    {
        $this->mapper = $mapper;
    }

    /**
     * 创建任务
     * @param array $data
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function startDetection(array $data):mixed
    {
        $lock = lock('contact_depup_task_'.$data['uid'],  4);
        try {
            $lock->block(3);
            $info = $this->mapper->findByUid($data['uid']);
            if(!empty($info)){
                return $info->id;
            }

            $robotIds = json_decode($data['robot_ids'],true);
            unset($data['robot_ids']);
            $model = ChContactDedupTask::create($data);

            $robotData = [];
            foreach ($robotIds as $key=>$robotId){
                $robotData[$robotId] = ['sort'=>$key];
            }
            $model->robots()->attach($robotData);

            $taskId = $model->id;
            if($taskId){
                container()->get(LowQueueService::class)->push(new CommonJob(new CommonJobDto(
                    classname: ChContactDedupService::class,
                    method: 'contactDetection',
                    data:[
                        'task_id' => $taskId
                    ]
                )));
            }
            return $taskId;
        } catch (LockTimeoutException $e) {
            // 无法获取锁...
            throw new NormalStatusException("检测中，请勿重复提交");
        } finally {
            optional($lock)->release();
        }
    }

    /**
     * 重新检测任务
     * @param array $data
     * @return \Hyperf\Tappable\HigherOrderTapProxy|int|mixed|null
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    #[Transaction]
    public function restartDetection(array $data): mixed
    {
        $lock = lock('restart_contact_depup_task_'.$data['uid'],  4);
        try {
            $lock->block(3);

            $info = $this->mapper->findById($data['id']);
            if(empty($info)){
                throw new NormalStatusException("操作失败，请刷新当前页面重试");
            }

            $robotInfo = $info->robots()->select(['id'])->get();

            $robotData = [];
            foreach ($robotInfo as $robot){
                $robotData[$robot->id] = ['sort'=>$robot->pivot->sort];
            }

            $taskData = [
                'uid' => $info->uid,
                'main_uid' => $info->main_uid,
            ];

            $info->robots()->detach();
            $info->forceDelete();

            $model = ChContactDedupTask::create($taskData);
            $model->robots()->attach($robotData);

            $taskId = $model->id;
            if($taskId){
                container()->get(LowQueueService::class)->push(new CommonJob(new CommonJobDto(
                    classname: ChContactDedupService::class,
                    method: 'contactDetection',
                    data:[
                        'task_id' => $taskId
                    ]
                )));
            }
            return $taskId;
        }catch (LockTimeoutException $e) {
            // 无法获取锁...
            throw new NormalStatusException("检测中，请勿重复提交");
        } finally {
            optional($lock)->release();
        }
    }

    /**
     * @param array $data
     * @return Model|Builder|ChContactDedupTask|array|\Hyperf\Database\Model\Builder
     */
    public function getTaskInfo(array $data): Model|Builder|ChContactDedupTask|array|\Hyperf\Database\Model\Builder
    {
        $info = ChContactDedupTask::query()
            ->select(['id','status','user_ids'])
            ->with(['robots'=>function ($query) {
                $query->select(['id','nickname','avatar','corp_name'])->orderBy('sort');
            }])
            ->where('id',$data['id'])
            ->first();

        if(!empty($info)){
            //重复客户数量
            $info['count'] = empty($info->user_ids) ? 0 : count($info->user_ids);
            unset($info->user_ids);
        }else{
            throw new NormalStatusException('任务不存在,请刷新当前页面重试');
        }

        return $info;
    }


    /**
     * 获取客户列表
     * @param array $data
     * @return array
     */
    public function repeatContactList(array $data): array
    {
        $info = ChContactDedupTask::query()
            ->select(['id','main_uid','status','user_ids'])
            ->where('id',$data['id'])
            ->first();
        if(empty($info) || empty($info->user_ids)){
            return [];
        }

        $robotIds = $info->robots()->pluck('id')->toArray();

        $list = $this->contactMapper->model::query()
            ->selectRaw('id,nickname,avatar,corp_short_name corp_name,user_id,robot_id,is_wechat')
            ->with(['robot'=>function ($query) {
                $query->select(['id','nickname','avatar','corp_name']);
            }])
            ->where('main_uid',$info->main_uid)
            ->whereIn('robot_id',$robotIds)
            ->whereIn('user_id',$info->user_ids)
            ->get();
        $list =  $list->groupBy('user_id') // 根据 user_id 分组
        ->map(function ($group) {
            // 获取组内的第一个用户数据
            $firstItem = $group->first();
            // 组装合并后的数据
            return [
                'id' => $firstItem['id'],
                'nickname' => $firstItem['nickname'],
                'avatar' => $firstItem['avatar'],
                'corp_name' => $firstItem['corp_name'],
                'user_id' => $firstItem['user_id'],
                'is_wechat' => $firstItem['is_wechat'],
                'robot' => $group->pluck('robot')->toArray(), // 获取所有机器人的信息
            ];
        })
            ->values() // 重置数组索引
            ->toArray(); // 转换为数组
        return $list;
    }

    /**
     * 取消任务
     * @param array $data
     * @return bool
     */
    #[Transaction]
    public function cancelDetection(array $data): bool
    {
        $model = $this->mapper->findById($data['id']);
        if($model){
            $model->robots()->detach();
            $model->forceDelete();
        }
        return true;
    }

    /**
     * 机器人权重设置
     * @param array $data
     * @return bool
     */
    public function sort(array $data): bool
    {
        /** @var ChContactDedupTask $model */
        $model = $this->mapper->findById($data['id']);
        if(empty($model)){
            return false;
        }

        $robotIds = json_decode($data['robot_ids'],true);
        $robotData = [];
        foreach ($robotIds as $key=>$robotId){
            $robotData[$robotId] = ['sort'=>$key];
        }

        $model->robots()->sync($robotData);
        return true;
    }

    /**
     * 去重操作
     * @param array $data
     * @return bool
     */
    public function deduplication(array $data): bool
    {
        $id = $data['id'];

        $lockKey = "contact_dedup_lock:" . $id;
        $lock = lock($lockKey, 4);
        try {
            $lock->block(2);

            /** @var ChContactDedupTask $model */
            $model = $this->mapper->findById($data['id']);
            if(empty($model)){
                return false;
            }

            if($model->status == ContactDedupTaskStatusConstants::STARTED){
                throw new NormalStatusException('任务处理中，请稍候再试');
            }

            if($model->status == ContactDedupTaskStatusConstants::COMPLETED){
                throw new NormalStatusException('任务已完成，请勿重复提交');
            }

            $model->status = ContactDedupTaskStatusConstants::STARTED;
            $model->save();

            $type = $data['type'];

            $robotData = $model->robots()->select(['id'])->orderBy('sort')->get();
            $robotIds = $robotData->pluck('id')->toArray();

            //重复的客户数据
            $userIds = $model->user_ids;

            $needDeduplicationData = [];
            if($type == 0){
                //随机保留一个企微号
                foreach ($userIds as $userId){
                    //需要保留的机器人ID
                    $retainRobotId = $robotIds[array_rand($robotIds)];
                    $delRobotIds = array_diff($robotIds,[$retainRobotId]);
                    $needDeduplicationData[] = [
                        'user_id' => $userId,
                        'robot_ids' => $delRobotIds
                    ];
                }
            }elseif($type == 1){
                //按优先级保留一个企微号
                array_shift($robotIds);
                foreach ($userIds as $userId){
                    //需要保留的机器人ID
                    $needDeduplicationData[] = [
                        'user_id' => $userId,
                        'robot_ids' => $robotIds
                    ];
                }
            }else{
                //手动选择保留的企微号
                $contactData = $data['contact_data'];
                $contactData = array_column($contactData,'robot_ids','user_id');
                foreach ($userIds as $userId){
                    if(isset($contactData[$userId])){
                        $delRobotIds = array_diff($robotIds,$contactData[$userId]);
                        if(!empty($delRobotIds)){
                            $needDeduplicationData[] = [
                                'user_id' => $userId,
                                'robot_ids' => $delRobotIds
                            ];
                        }
                    }
                }
            }

            if(!empty($needDeduplicationData)){
                $needDeduplicationData = $this->transformData($needDeduplicationData);
                container()->get(LowQueueService::class)->push(new CommonJob(new CommonJobDto(
                    classname: ChContactDedupService::class,
                    method: 'contactDeduplication',
                    data:[
                        'id' => $model->id,
                        'needDeduplicationData' => $needDeduplicationData
                    ]
                )));
            }
            return true;
        } catch (LockTimeoutException $e) {
            return false;
        }finally {
            $lock->release();
        }
    }

    private function transformData(array $input): array
    {
        // 使用生成器逐步处理数据
        $result = [];

        foreach ($input as $item) {
            foreach ($item['robot_ids'] as $robotId) {
                // 使用引用避免不必要的数组查找
                if (!isset($result[$robotId])) {
                    $result[$robotId] = [
                        'robot_id' => $robotId,
                        'user_ids' => []
                    ];
                }
                // 添加用户 ID
                $result[$robotId]['user_ids'][] = $item['user_id'];
            }
        }

        // 重新索引结果数组并返回
        return array_values($result);
    }


    /**
     * 检测重复 异步队列用到的方法
     * @param array $data
     * @return void
     */
    public function contactDetection(array $data): void
    {
        if(empty($data['task_id'])){
            return;
        }
        $taskInfo = $this->mapper->findById($data['task_id']);
        if(empty($taskInfo)){
            return;
        }
        $robotIds = $taskInfo->robots()->pluck('id')->toArray();
        $userIds = $this->contactMapper->getDuplicateUserIds($robotIds);
        //检测完成
        $this->mapper->update($taskInfo->id,[
            'status' => ContactDedupTaskStatusConstants::DETECTED,
            'user_ids' => $userIds
        ]);
    }

    /**
     * 客户去重 异步队列用到的方法
     * @param array $data
     * @return void
     */
    public function contactDeduplication(array $data): void
    {
        if(empty($data['needDeduplicationData'])){
            return;
        }
        $needDeduplicationData = $data['needDeduplicationData'];

        $count = count($needDeduplicationData);
        $id = $data['id'];
        foreach ($needDeduplicationData as $k=>$item){
            if($k+1 == $count){
                $isLast = true;
            }else{
                $isLast = false;
            }
            co(function () use ($item,$id,$isLast){
                $this->realDeduplication($item,$id,$isLast);
            });
        }
    }

    /**
     * 真实去重，删除好友
     * @param array $data
     * @return void
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function realDeduplication(array $data,int $id,bool $isLast): void
    {
        $robotId = $data['robot_id'];
        $userIds = $data['user_ids'];
        $robotInfo = $this->robotMapper->findById($robotId);
        if(empty($robotInfo)){
            if($isLast){
                $this->changeTaskStatus($id,ContactDedupTaskStatusConstants::FAIL);
            }
            return;
        }
        if($robotInfo->login_status != 1){
            //机器人离线
            if($isLast){
                $this->changeTaskStatus($id,ContactDedupTaskStatusConstants::FAIL);
            }
            return;
        }

        $robotService = make(Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
        $now = Carbon::now()->toDateTimeString();
        $logData = [];

        foreach ($userIds as $userId){
            $result = $robotService->deleteContact($userId);
            if($result !== false){
                $ids = ChContact::query()
                    ->where('user_id',$userId)
                    ->pluck('id')
                    ->toArray();

                foreach ($ids as $id){
                    $logData[] = [
                        'contact_id' => $id,
                        'remark' => "企微号【{$robotInfo->nickname}】去重删除",
                        'type' => ContactBehaviorTypeConstants::OTHER,
                        'created_at' => $now,
                    ];
                }
            }
            Coroutine::sleep(randomFloat(1,5));
        }

        if(!empty($logData)){
            ChContactBehaviorLog::insert($logData);
        }
        if($isLast){
            $this->changeTaskStatus($id,ContactDedupTaskStatusConstants::COMPLETED);
        }
    }

    private function changeTaskStatus(int $id,int $status): void
    {
        $this->mapper->model::query()
            ->where('id',$id)
            ->update([
                'status' => $status
            ]);
    }
}